<script setup lang="ts">
import { computed, onMounted, reactive, ref } from 'vue';
import { api } from '../lib/api';
import type { PageResponse } from '../lib/api';
import type { ${classInfo.className}, ${classInfo.className}PrimaryKey } from '../types/${classInfo.className}';

defineOptions({
  name: '${frontendPageComponentName}',
});

const API_BASE = '/${apiBasePath}';
const PRIMARY_FIELD = '${options.primaryFieldName?default(classInfo.fieldList[0].fieldName)}';
const moduleDisplayName = '${moduleDisplayName?js_string}';
const moduleSummary = '${moduleSummary?js_string}';

<#function resolveFieldKind field>
  <#assign javaType = (field.fieldClass!"String")?lower_case>
  <#if javaType?contains("bigdecimal") || javaType?contains("decimal") || javaType?contains("double") || javaType?contains("float")>
    <#return "number">
  <#elseif javaType?contains("long") || javaType?contains("int") || javaType?contains("short") || javaType?contains("byte")>
    <#return "number">
  <#elseif javaType?contains("bool")>
    <#return "boolean">
  <#elseif javaType?contains("localdatetime") || javaType?contains("timestamp")>
    <#return "datetime">
  <#elseif javaType?contains("localdate") || javaType?contains("date")>
    <#return "date">
  <#else>
    <#return "string">
  </#if>
</#function>

<#function fieldLabel field>
  <#if field.fieldComment?has_content>
    <#return field.fieldComment?replace('\'', "\\'")?replace("\n", " ")>
  <#else>
    <#return field.fieldName>
  </#if>
</#function>

<#function fieldNameByColumn columnName>
  <#if !columnName?has_content>
    <#return "">
  </#if>
  <#assign normalized = columnName?lower_case>
  <#list classInfo.fieldList as field>
    <#if (field.columnName!"")?lower_case == normalized>
      <#return field.fieldName>
    </#if>
  </#list>
  <#return columnName>
</#function>

<#function fieldLabelByColumn columnName>
  <#if !columnName?has_content>
    <#return "">
  </#if>
  <#assign normalized = columnName?lower_case>
  <#list classInfo.fieldList as field>
    <#if (field.columnName!"")?lower_case == normalized>
      <#return fieldLabel(field)>
    </#if>
  </#list>
  <#return columnName>
</#function>

<#assign numberFields = []>
<#assign booleanFields = []>
<#assign dateFields = []>
<#assign primaryFieldName = options.primaryFieldName?default(classInfo.fieldList[0].fieldName)>
<#list classInfo.fieldList as field>
  <#assign kind = resolveFieldKind(field)>
  <#if kind == "number">
    <#assign numberFields = numberFields + [field.fieldName]>
  <#elseif kind == "boolean">
    <#assign booleanFields = booleanFields + [field.fieldName]>
  <#elseif kind == "datetime" || kind == "date">
    <#assign dateFields = dateFields + [field.fieldName]>
  </#if>
</#list>

const fieldConfigs = [
<#list classInfo.fieldList as field>
  {
    name: '${field.fieldName}',
    label: '${fieldLabel(field)}',
    isPrimary: ${(field.fieldName == primaryFieldName)?c},
    includeInForm: ${(field.fieldName != primaryFieldName)?c},
    type: '${resolveFieldKind(field)}'
  }<#if field_has_next>,</#if>
</#list>
];

const numberFieldSet = new Set<string>([
<#if numberFields?size gt 0>
  '${numberFields?join("', '")}'
</#if>
]);
const booleanFieldSet = new Set<string>([
<#if booleanFields?size gt 0>
  '${booleanFields?join("', '")}'
</#if>
]);
const dateFieldSet = new Set<string>([
<#if dateFields?size gt 0>
  '${dateFields?join("', '")}'
</#if>
]);

const compositeUniqueGroups = [
<#if compositeUniqueIndexes?has_content>
  <#list compositeUniqueIndexes as uniqueIndex>
    {
      key: ${((uniqueIndex.name?has_content)?then(uniqueIndex.name, 'composite_' + uniqueIndex_index))?js_string},
      fieldNames: [
        <#list uniqueIndex.columnNames![] as column>
          ${fieldNameByColumn(column)?js_string}<#if column_has_next>,</#if>
        </#list>
      ],
      fieldLabels: [
        <#list uniqueIndex.columnNames![] as column>
          ${fieldLabelByColumn(column)?js_string}<#if column_has_next>,</#if>
        </#list>
      ]
    }<#if uniqueIndex_has_next>,</#if>
  </#list>
</#if>
];

const compositeUniqueFieldSet = new Set<string>(
  compositeUniqueGroups.reduce<string[]>((acc, group) => acc.concat(group.fieldNames), []),
);

const PAGE_SIZE_OPTIONS = [10, 20, 50];
const DEFAULT_PAGE_SIZE = PAGE_SIZE_OPTIONS[0];

const items = ref<${classInfo.className}[]>([]);
const loading = ref(false);
const error = ref<string | null>(null);
const editingId = ref<${classInfo.className}PrimaryKey | null>(null);
const form = reactive<Record<string, any>>(buildEmptyForm());
const filters = reactive<Record<string, any>>(buildEmptyFilters());
const pagination = reactive({ page: 1, size: DEFAULT_PAGE_SIZE, total: 0 });

const formFields = computed(() => fieldConfigs.filter((field) => field.includeInForm));
const filterFields = computed(() => fieldConfigs);
const totalPages = computed(() => Math.max(1, Math.ceil((pagination.total || 0) / pagination.size)));

function findIncompleteCompositeGroup(source: Record<string, any>) {
  for (const group of compositeUniqueGroups) {
    const values = group.fieldNames.map((name) => source[name]);
    const filledCount = values.filter((value) => value !== '' && value !== null && value !== undefined).length;
    if (filledCount > 0 && filledCount < group.fieldNames.length) {
      return group;
    }
  }
  return null;
}

onMounted(() => {
  void loadData(1, DEFAULT_PAGE_SIZE, filters);
});

async function loadData(
  targetPage = pagination.page,
  targetSize = pagination.size,
  overrideFilters: Record<string, any> = filters,
) {
  loading.value = true;
  error.value = null;
  try {
    const params = buildQueryParams(overrideFilters, targetPage, targetSize);
    const data = await api.get<PageResponse<${classInfo.className}> | ${classInfo.className}[]>(API_BASE, params);
    if (Array.isArray(data)) {
      items.value = data;
      pagination.page = targetPage;
      pagination.size = targetSize;
      pagination.total = data.length;
      return;
    }
    const records = Array.isArray(data.records) ? data.records : [];
    items.value = records;
    pagination.page = Number(data.page ?? targetPage);
    pagination.size = Number(data.size ?? targetSize);
    pagination.total = Number(data.total ?? records.length);
  } catch (err) {
    error.value = err instanceof Error ? err.message : '加载数据失败';
  } finally {
    loading.value = false;
  }
}

function buildEmptyForm() {
  const initial: Record<string, any> = {};
  fieldConfigs.forEach((field) => {
    if (field.includeInForm) {
      initial[field.name] = '';
    }
  });
  return initial;
}

function buildEmptyFilters() {
  const initial: Record<string, any> = {};
  fieldConfigs.forEach((field) => {
    initial[field.name] = '';
  });
  return initial;
}

function resetForm() {
  Object.assign(form, buildEmptyForm());
  editingId.value = null;
}

function resetFilters() {
  Object.assign(filters, buildEmptyFilters());
}

function buildQueryParams(source: Record<string, any>, page: number, size: number) {
  const params: Record<string, unknown> = { page, size };
  fieldConfigs.forEach((field) => {
    const raw = source[field.name];
    if (raw === '' || raw === undefined) {
      return;
    }
    if (numberFieldSet.has(field.name)) {
      const value = Number(raw);
      if (!Number.isNaN(value)) {
        params[field.name] = value;
      }
      return;
    }
    if (booleanFieldSet.has(field.name)) {
      if (raw === 'true' || raw === true) {
        params[field.name] = true;
      } else if (raw === 'false' || raw === false) {
        params[field.name] = false;
      }
      return;
    }
    params[field.name] = raw;
  });
  return params;
}

function toInputValue(fieldName: string, value: unknown) {
  if (value === undefined || value === null) {
    return '';
  }
  if (booleanFieldSet.has(fieldName)) {
    return value ? 'true' : 'false';
  }
  if (dateFieldSet.has(fieldName) && typeof value === 'string') {
    return value.replace(' ', 'T');
  }
  return String(value);
}

function sanitizePayload(source: Record<string, any>) {
  const payload: Record<string, any> = {};
  formFields.value.forEach((field) => {
    const raw = source[field.name];
    if (raw === '' || raw === undefined) {
      payload[field.name] = null;
      return;
    }
    if (numberFieldSet.has(field.name)) {
      payload[field.name] = Number(raw);
      return;
    }
    if (booleanFieldSet.has(field.name)) {
      payload[field.name] = raw === true || raw === 'true';
      return;
    }
    payload[field.name] = raw;
  });
  return payload;
}

async function handleSubmit(event: Event) {
  event.preventDefault();
  error.value = null;
  const incompleteGroup = findIncompleteCompositeGroup(form);
  if (incompleteGroup) {
    error.value = `请同时填写：${'$'}{incompleteGroup.fieldLabels.join('、')}`;
    return;
  }
  loading.value = true;
  try {
    const payload = sanitizePayload(form);
    if (editingId.value !== null) {
      await api.put(`${'$'}{API_BASE}/${'$'}{editingId.value}`, payload);
    } else {
      await api.post(API_BASE, payload);
    }
    resetForm();
    await loadData(pagination.page, pagination.size);
  } catch (err) {
    error.value = err instanceof Error ? err.message : '提交失败';
  } finally {
    loading.value = false;
  }
}

function handleEdit(record: ${classInfo.className}) {
  const next: Record<string, any> = {};
  formFields.value.forEach((field) => {
    const value = (record as Record<string, any>)[field.name];
    next[field.name] = toInputValue(field.name, value);
  });
  Object.assign(form, buildEmptyForm(), next);
  editingId.value = (record as Record<string, any>)[PRIMARY_FIELD] as ${classInfo.className}PrimaryKey;
}

async function handleDelete(id: ${classInfo.className}PrimaryKey) {
  loading.value = true;
  error.value = null;
  try {
    const nextPage = items.value.length <= 1 && pagination.page > 1 ? pagination.page - 1 : pagination.page;
    await api.remove(`${'$'}{API_BASE}/${'$'}{id}`);
    if (editingId.value === id) {
      resetForm();
    }
    await loadData(nextPage, pagination.size);
  } catch (err) {
    error.value = err instanceof Error ? err.message : '删除失败';
  } finally {
    loading.value = false;
  }
}

function handleFilterSubmit() {
  const currentSize = pagination.size;
  pagination.page = 1;
  void loadData(1, currentSize);
}

function handleFilterReset() {
  const currentSize = pagination.size;
  resetFilters();
  pagination.page = 1;
  void loadData(1, currentSize, filters);
}

function handlePageSizeChange(event: Event) {
  const target = event.target as HTMLSelectElement;
  const nextSize = Number(target.value) || DEFAULT_PAGE_SIZE;
  pagination.page = 1;
  pagination.size = nextSize;
  void loadData(1, nextSize);
}

function handlePageChange(nextPage: number) {
  if (nextPage < 1 || nextPage > totalPages.value) {
    return;
  }
  void loadData(nextPage, pagination.size);
}

function inputType(field: { name: string; type: string }) {
  if (booleanFieldSet.has(field.name)) {
    return 'select';
  }
  if (field.type === 'number') {
    return 'number';
  }
  if (field.type === 'datetime') {
    return 'datetime-local';
  }
  if (field.type === 'date') {
    return 'date';
  }
  return 'text';
}

function renderCellValue(fieldName: string, value: unknown) {
  if (value === undefined || value === null) {
    return '-';
  }
  if (booleanFieldSet.has(fieldName)) {
    return value ? '是' : '否';
  }
  if (dateFieldSet.has(fieldName) && typeof value === 'string') {
    return value.replace('T', ' ');
  }
  return String(value);
}
</script>

<template>
  <div class="page-stack">
    <section class="card">
      <div class="page-header">
        <div>
          <h2>{{ moduleDisplayName }} 数据管理</h2>
          <p>{{ moduleSummary }}</p>
        </div>
        <div class="page-actions">
          <button type="button" class="secondary" @click="resetForm">重置表单</button>
          <button
            type="button"
            class="pill-button ghost"
            @click="loadData(pagination.page, pagination.size)"
            :disabled="loading"
          >
            刷新数据
          </button>
        </div>
      </div>
      <div v-if="compositeUniqueGroups.length > 0" class="info-banner" style="margin-top: 16px">
        <strong>💡 提示：</strong>
        <span style="margin-left: 8px">
          <span v-for="(group, idx) in compositeUniqueGroups" :key="group.key">
            <span v-if="idx > 0">；</span>
            <strong>{{ group.fieldLabels.join(' + ') }}</strong> 的组合不能重复
          </span>
        </span>
      </div>
      <div v-if="error" style="color: #ef4444; margin-top: 12px">{{ error }}</div>
    </section>

    <section class="panel">
      <h3 style="margin-top: 0">筛选条件</h3>
      <form @submit.prevent="handleFilterSubmit">
        <div class="form-grid">
          <div v-for="field in filterFields" :key="'filter-' + field.name" class="form-field">
            <label :for="'filter-' + field.name">{{ field.label }}</label>
            <select
              v-if="booleanFieldSet.has(field.name)"
              :id="'filter-' + field.name"
              v-model="filters[field.name]"
            >
              <option value="">全部</option>
              <option value="true">是</option>
              <option value="false">否</option>
            </select>
            <input
              v-else
              :id="'filter-' + field.name"
              v-model="filters[field.name]"
              :type="inputType(field)"
              :placeholder="`请输入${'$'}{field.label}`"
            />
          </div>
        </div>
        <div class="page-actions" style="margin-top: 12px">
          <button type="submit" class="primary" :disabled="loading">查询</button>
          <button type="button" class="secondary" @click="handleFilterReset" :disabled="loading">
            重置筛选
          </button>
        </div>
      </form>
    </section>

    <section class="panel">
      <h3 style="margin-top: 0">{{ editingId === null ? '新增' + moduleDisplayName : '编辑' + moduleDisplayName }}</h3>
      <form @submit="handleSubmit">
        <div class="form-grid">
          <div v-for="field in formFields" :key="field.name" class="form-field">
            <label :for="field.name">
              {{ field.label }}
            </label>
            <select
              v-if="booleanFieldSet.has(field.name)"
              :id="field.name"
              v-model="form[field.name]"
            >
              <option value="">请选择</option>
              <option value="true">是</option>
              <option value="false">否</option>
            </select>
            <input
              v-else
              :id="field.name"
              v-model="form[field.name]"
              :type="inputType(field)"
              :placeholder="`请输入${'$'}{field.label}`"
            />
          </div>
        </div>
        <div class="page-actions" style="margin-top: 12px">
          <button type="submit" class="primary" :disabled="loading">
            {{ editingId === null ? '保存并创建' : '保存更新' }}
          </button>
          <button v-if="editingId !== null" type="button" class="secondary" @click="resetForm">取消编辑</button>
        </div>
      </form>
    </section>

    <section class="panel">
      <div class="page-header" style="margin-bottom: 16px">
        <div>
          <h3 style="margin: 0">数据列表</h3>
          <p style="margin: 6px 0 0; color: #64748b; font-size: 13px">支持分页、条件筛选、行级编辑与删除操作。</p>
        </div>
        <span style="font-size: 13px; color: #94a3b8">共 {{ pagination.total }} 条记录</span>
      </div>
      <div style="overflow-x: auto">
        <table>
          <thead>
            <tr>
              <th v-for="field in fieldConfigs" :key="field.name">{{ field.label }}</th>
              <th style="width: 160px">操作</th>
            </tr>
          </thead>
          <tbody>
            <tr v-if="items.length === 0">
              <td :colspan="fieldConfigs.length + 1" style="text-align: center; padding: 24px">暂无数据</td>
            </tr>
            <tr v-for="record in items" :key="String((record as any)[PRIMARY_FIELD])">
              <td v-for="field in fieldConfigs" :key="field.name">
                {{ renderCellValue(field.name, (record as any)[field.name]) }}
              </td>
              <td>
                <div class="table-actions">
                  <button type="button" class="secondary" @click="handleEdit(record)">编辑</button>
                  <button
                    type="button"
                    class="danger"
                    @click="handleDelete((record as any)[PRIMARY_FIELD] as ${classInfo.className}PrimaryKey)"
                    :disabled="loading"
                  >
                    删除
                  </button>
                </div>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
      <div class="table-pagination">
        <div style="color: #64748b; font-size: 13px">第 {{ pagination.page }} / {{ totalPages }} 页</div>
        <div style="display: flex; align-items: center; gap: 12px">
          <label for="page-size" style="display: flex; align-items: center; gap: 8px">
            <span>每页</span>
            <select
              id="page-size"
              :value="pagination.size"
              @change="handlePageSizeChange"
              :disabled="loading"
            >
              <option v-for="option in PAGE_SIZE_OPTIONS" :key="option" :value="option">
                {{ option }}
              </option>
            </select>
          </label>
          <button
            type="button"
            class="secondary"
            @click="handlePageChange(pagination.page - 1)"
            :disabled="loading || pagination.page <= 1"
          >
            上一页
          </button>
          <button
            type="button"
            class="secondary"
            @click="handlePageChange(pagination.page + 1)"
            :disabled="loading || pagination.page >= totalPages"
          >
            下一页
          </button>
        </div>
      </div>
    </section>
  </div>
</template>

